"""
pagamiago - free software script to send PayPal payments

Copyright (C) 2018 Alyssa Rosenzweig

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import json
import re
import urllib.parse
import math
import os
import codecs

from pagamigo.RequestSession import RequestSession
from pagamigo.captcha import handle_captcha
from pagamigo.helpers import grep_json_string

ID = "redirect"
DESCRIPTION = "Pay a web store via PayPal (redirect flow)"

ARGS = {
    "url": ("URL (copy it from your browser)", "URL", None),
    "base": ("Base URL", "base", None),
    "country": ("Country code", "country", None),
    "lang": ("Language code", "language", None)
}

# The base URL is usually just the, well, base URL. But occassionally it will
# get thrown in the hash, if we're coming from the noscript pagamigo splash
# page. Deal with that recursively

def extract_base_url(url):
    parts = urllib.parse.urlparse(url)
    base = parts.scheme + "://" + parts.netloc

    if parts.netloc in ["www.paypal.com", "sandbox.paypal.com", "paypal.com"]:
        return base
    elif parts.fragment:
        return extract_base_url(parts.fragment)
    else:
        return base


def generate_random_hex_bytes(count, mock):
    if mock:
        return "0" * count
    else:
        o = codecs.encode(os.urandom(math.ceil(count / 2)), "hex").decode()
        return o[:-1] if (count % 2) else o


def update_meta(session, state):
    session.meta["state"] = state
    session.meta["csci"] = generate_random_hex_bytes(32, session.mock)
    session.meta["calc"] = generate_random_hex_bytes(13, session.mock)


def reduced_fields(obj, f):
    return {k: obj[k] for k in obj if k in f}


def amount_to_json(amount):
    return {
        "currency_code": amount["currency_code"],
        "value": int(float(amount["amount"]) * 100.0)
    }


def flow(session, url, base, country, lang):
    extracted_base = extract_base_url(url)

    session.set_module(url[len(base):], "Redirect flow", 11)

    portal = session.request(url[len(base):], False, {
    }, mock_resp="resps/portal.html", plain_headers=True)

    # The portal may be CAPTCHA walled for reasons that are not entirely
    # clear; handle that here
    portal = handle_captcha(session, portal, mock=session.mock)

    # Grep out what we can from the portal

    token = grep_json_string(portal, "token")
    merchant_id = grep_json_string(portal, "merchantId")
    merchant_country = grep_json_string(portal, "merchantCountry")
    uid = grep_json_string(portal, "buyer_id")
    return_url = grep_json_string(portal, "return_url")

    amounts_text = re.search(
        '"amounts": (.*?\n            })',
        portal,
        re.MULTILINE | re.DOTALL).group(1)
    amount_json = json.loads(amounts_text)

    if "shipping" not in amount_json:
        amount_json["shipping"] = {
            "currency_code": amount_json["total"]["currency_code"],
            "amount": 0
        }

    # The nontrivial requests attach metadata; we can now generate that

    session.meta = {
        "token": token,
        "calc": "",
        "csci": "",
        "locale": {
            "country": country,
            "language": lang
        },
        "state": "",
        "app_name": "hermesnodeweb"
    }

    update_meta(session, "ui_checkout_landing")

    session.request("/webapps/hermes/api/checkout/" + token + "/flowEligibility",
                    False, {"localeCountry": country, "braintreeInegration": "", })

    update_meta(session, "ui_checkout_login")

    session.request(
        "/webapps/hermes/api/info/tokenizedWallet/" + token, False, {})

    session.request("/webapps/hermes/api/batch/setbuyer", True, {
        "data": {
            "eConsent": {
                "method": "get",
                "uri": "/api/user/eConsent",
                "params": {},
                "dependencies": []
            },
            "memberSegment": {
                "method": "post",
                "uri": "/api/segment/memberSegment",
                "data": {
                    "page": "HERMES_RYI_PAGE",
                    "site": "HERMES_RYI_PAGE",
                    "locale": "en_US",
                    "puType": "PERSONAL",
                    "visit_id": token,
                    "si": token,
                    "payment_context": {
                        "business_case": "NONE",
                        "business_case_variation": "",
                        "validate_existing_offer": "false",
                        "total_transaction_amount": amount_to_json(amount_json["total"]),
                        "total_shipping_amount": amount_to_json(amount_json["shipping"]),
                        "payment_units": [
                            {
                                "transaction_amount": amount_to_json(amount_json["total"]),
                                "transaction_id": "",
                                "encrypted_transaction_id": "",
                                "activity_id": "",
                                "payee": {
                                    "pu_type": "BUSINESS",
                                    "country_code": merchant_country,
                                    "epu": merchant_id,
                                    "pu": ""
                                }
                            }
                        ]
                    },
                    "target_placements": [
                        {
                            "placement_code": "210005",
                            "presentation_types": [
                                "decision"
                            ]
                        }
                    ]
                },
                "params": {},
                "dependencies": []
            },
            "createCheckoutSession": {
                "method": "post",
                "uri": "/api/checkout/" + token + "/session/create",
                "data": {},
                "params": {},
                "dependencies": [
                    "eConsent"
                ]
            }
        },
    })

    session.request("/webapps/hermes/api/checkout/" + token +
                    "/session/create", True, {"data": {}, })

    update_meta(session, "ui_checkout_review")

    session.request(
        "/webapps/hermes/api/pxp/xo_hermes_choice_variant", False, {})

    session.request("/webapps/hermes/api/pxp/xo_hermes_use_cep_credit_offer",
                    False, {"uid": uid, "ctx.COUNTRY": country, })

    session.request("/webapps/hermes/api/pxp/xo_mors_hermes_placements_ryi",
                    False, {"ctx.COUNTRY": country, })

    session.request(
        "/webapps/hermes/api/pxp/xo_hermesnodeweb_us_ppc_asset_lite_ramp",
        False,
        {})

    session.request("/webapps/hermes/api/checkout/" + token + "/installmentOptions/", True, {
        "data": {
            "checkoutAppData": {
                "merchant": {
                    "merchant_country": merchant_country,
                }
            },
            "checkoutCart": {
                "purchase": {
                    "amounts": {
                        "total": reduced_fields(amount_json["total"], ["amount", "currency_code"]),
                    }
                }
            },
            "payee": {
                "merchant": {
                    "id": merchant_id
                }
            },
            "isMemberCredit": True
        },
    })

    session.request("/webapps/hermes/api/checkout/" + token +
                    "/session/authorize", True, {"data": {}, })

    print("\nTransaction complete! To confirm, navigate in your browser to:")
    print(return_url)
